﻿@page "/agents"
@using Gurux.DLMS.AMI.Shared
@using Gurux.DLMS.AMI.Shared.DIs
@using Gurux.DLMS.AMI.Shared.DTOs.Agent
@using Gurux.DLMS.AMI.Shared.DTOs.Enums
@using Gurux.DLMS.AMI.Shared.Enums
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@using Gurux.DLMS.AMI.Shared.DTOs
@using Gurux.DLMS.AMI.Shared.Rest
@using Microsoft.AspNetCore.SignalR.Client
@using Gurux.DLMS.AMI.Client.Helpers
@using Gurux.DLMS.AMI.Module
@using System.ComponentModel;
@using System.ComponentModel.DataAnnotations

@attribute [Authorize(Roles = "Admin, AgentManager")]
@inject NavigationManager NavigationManager
@inject HttpClient Http
@inject IGXNotifier Notifier
@inject ConfirmBase Confirm
@implements IDisposable
@implements IAmiComponent

@if (Menu)
{
    <AuthorizeView Roles=@GXRoles.ToString(GXRoles.Admin, GXRoles.AgentManager)>
        <MenuControl RightCorner="true">
            <ChildContent>
                <MenuItem Text="@Properties.Resources.Update" Icon="oi oi-loop-circular" OnClick="@OnUpdateVersion" />
                <MenuItem Text="@Properties.Resources.Add" Icon="oi oi-plus" OnClick="@OnAdd" />
                <MenuItem Text="@Properties.Resources.Remove" Icon="oi oi-trash" OnClick="@OnRemove" />
            </ChildContent>
        </MenuControl>
    </AuthorizeView>
}

<GXTable @ref="table"
         Context="agent"
         ItemsProvider="@GetItems"
         SelectionMode="SelectionMode.Multiple"
         Filter="@Filter"
         ShowAllUsers="@Header"
         ShowRemoved="@Header"
         Columns="@Columns"
         Total="@Total"
         Menu="@Menu"
         OnSearch="@Updated">
    <FilterContent>
        <th>
            <input class="form-control" type="text" placeholder="@Properties.Resources.FilterByName"
                   @onchange="@((ChangeEventArgs __e) => filter.Name = Convert.ToString(__e.Value))" />
        </th>
        <th>
            <input class="form-control" placeholder="Filter by generation..."
                   type="datetime-local"
                   @onchange="@((ChangeEventArgs __e) => filter.CreationTime = @StatusTile.ToDateTime(__e.Value))" />
        </th>
        <th>
            <input class="form-control" placeholder="Filter by detected..."
                   type="datetime-local"
                   @onchange="@((ChangeEventArgs __e) => filter.Detected = @StatusTile.ToDateTimeOffset(__e.Value))" />
        </th>
        <th>
            <!--Status-->
        </th>
        <th>
            <input class="form-control" placeholder="Filter by version..."
                   type="datetime-local"
                   @onchange="@((ChangeEventArgs __e) => filter.Version = Convert.ToString(__e.Value))" />
        </th>
    </FilterContent>
    <MenuContent>
        <AuthorizeView Roles=@GXRoles.ToString(GXRoles.Admin, GXRoles.AgentManager)>
            <ContextMenuItem Text="@Properties.Resources.Edit" Icon="oi oi-pencil" OnClick="@OnEdit"></ContextMenuItem>
            <ContextMenuItem Text="@Properties.Resources.Update" Icon="oi oi-loop-circular" OnClick="@OnUpdateVersion"></ContextMenuItem>
            <ContextMenuItem Text="@Properties.Resources.Remove" Icon="oi oi-trash" OnClick="@OnRemove"></ContextMenuItem>
        </AuthorizeView>
    </MenuContent>
    <HeaderContent>
        <Th Id="Name">@Properties.Resources.Name</Th>
            <Th Id="CreationTime" SortMode="SortMode.Descending">@Properties.Resources.CreationTime</Th>
            <Th Id="Detected">@Properties.Resources.Detected</Th>
            <Th Id="Status">@Properties.Resources.Status</Th>
            <Th Id="Version">@Properties.Resources.Version</Th>
        </HeaderContent>
        <ItemContent>
            <Td Link="@("Agent/Edit/" + agent.Id)"><span class=@(ClientHelpers.GetActiveDot(agent.Active))></span>@agent.Name</Td>
            <td>@agent.CreationTime</td>
        <td>@agent.Detected</td>
        <td>@agent.Status</td>
        @if (IsNewVersionAvailable(agent))
        {
            <td><Tooltip Title=@string.Format("Version {0} is available.", agent.AvailableVersion)>@GetVersion(agent.Version) <span class="oi oi-loop-circular" aria-hidden="true"></span></Tooltip></td>
        }
        else
        {
            <td>@GetVersion(agent.Version)</td>
        }
    </ItemContent>
</GXTable>
<br />
<Confirm @ref="DeleteConfirmation"
         ConfirmationChanged="OnDeleteConfirmation"
         OkTitle="@Properties.Resources.Remove"
         ConfirmationMessage=""
         ConfirmationTitle="@Properties.Resources.AreYouSureYouWantToDeleteSelectedItems">
</Confirm>
<Confirm @ref="UpdateConfirmation"
         ConfirmationChanged="OnUpdateConfirmation"
         OkTitle="@Properties.Resources.Update"
         AllowDelete="false"
         ConfirmationTitle="@Properties.Resources.ConfirmUpdate"
         ConfirmationMessage="@Properties.Resources.AgentUpdates">
</Confirm>

@code {
    /// <summary>
    /// Selected agent ID.
    /// </summary>
    [Parameter]
    public Guid? Id { get; set; }

    private bool IsNewVersionAvailable(GXAgent agent)
    {
        if (string.IsNullOrEmpty(agent.Version) ||
            !string.IsNullOrEmpty(agent.UpdateVersion) ||
            agent.Version == agent.AvailableVersion)
        {
            return false;
        }
        return true;
    }

    /// <inheritdoc />
    public string Name
    {
        get
        {
            return Gurux.DLMS.AMI.Client.Properties.Resources.Agents;
        }
    }

    /// <inheritdoc />
    public Type? ConfigurationUI
    {
        get
        {
            return null;
        }
    }

    /// <inheritdoc />
    public string? Icon
    {
        get
        {
            return "oi oi-wifi";
        }
    }

    /// <summary>
    /// Amount of the agents shown on the view.
    /// </summary>
    [Parameter]
    [Display(Name = "AmountOfTheRowsShownOnTheView", ResourceType = typeof(Properties.Resources))]
    public int Count { get; set; } = 0;

    /// <summary>
    /// Is filter shown.
    /// </summary>
    [Parameter]
    [Display(Name = "ShowFilter", ResourceType = typeof(Properties.Resources))]
    public bool Filter { get; set; } = true;

    /// <summary>
    /// Is header shown.
    /// </summary>
    [Parameter]
    public bool Header { get; set; } = true;

    /// <summary>
    /// Is edit allowed.
    /// </summary>
    [Parameter]
    public bool CanEdit { get; set; } = true;

    /// <summary>
    /// Available columns.
    /// </summary>
    [Parameter]
    public string[]? Columns { get; set; }

    /// <summary>
    /// Show total count.
    /// </summary>
    [Parameter]
    [Display(Name = "ShowTotal", ResourceType = typeof(Properties.Resources))]
    public bool Total { get; set; } = true;

    /// <summary>
    /// Is menu shown.
    /// </summary>
    [Parameter]
    [Display(Name = "ShowMenu", ResourceType = typeof(Properties.Resources))]
    public bool Menu { get; set; } = true;

    private string? GetVersion(string? version)
    {
        if (version == null)
        {
            return Properties.Resources.CustomBuild;
        }
        return version;
    }

    /// <summary>
    /// Agent filter.
    /// </summary>
    private GXAgent filter = new GXAgent(null)
        {
            Template = false
        };

    /// <summary>
    /// Verify that agant can be removed.
    /// </summary>
    protected ConfirmBase? DeleteConfirmation;

    //Verify that agent can be updated.
    protected ConfirmBase? UpdateConfirmation;

    /// <summary>
    /// Reference to the table.
    /// </summary>
    private GXTable<GXAgent>? table;

    /// <summary>
    /// Update table.
    /// </summary>
    /// <returns></returns>
    protected async Task Updated()
    {
        try
        {
            if (table != null)
            {
                Notifier?.ClearStatus();
                await table.RefreshDataAsync(true);
            }
        }
        catch (Exception ex)
        {
            Notifier?.ProcessError(ex);
        }
    }

    /// <summary>
    /// Agent status has changed.
    /// </summary>
    /// <param name="agent">Changed agent.</param>
    protected void StateChanged(IEnumerable<GXAgent> list)
    {
        try
        {
            if (table != null && table.Items != null)
            {
                foreach (var agent in list)
                {
                    foreach (var it in table.Items)
                    {
                        if (it.Id == agent.Id)
                        {
                            it.Status = agent.Status;
                            if (agent.Detected != null)
                            {
                                it.Detected = agent.Detected;
                            }
                            if (agent.AvailableVersion != null)
                            {
                                it.AvailableVersion = agent.AvailableVersion;
                            }
                            break;
                        }
                    }
                }
                StateHasChanged();
            }
        }
        catch (Exception)
        {
        }
    }

    protected override async Task OnInitializedAsync()
    {
        //Wait until table is loaded. Don't remove this or table is null and last settings are not available.
        await Task.Delay(1);
        try
        {
            if (Notifier == null)
            {
                throw new ArgumentException(Properties.Resources.InvalidNotifier);
            }
            Notifier.ClearStatus();
            Notifier.On<IEnumerable<GXAgent>>(this, nameof(IGXHubEvents.AgentUpdate), async (agents) =>
            {
                await Updated();
            });
            Notifier.On<IEnumerable<GXAgent>>(this, nameof(IGXHubEvents.AgentDelete), async (agents) =>
            {
                await Updated();
            });
            Notifier.On<IEnumerable<GXAgent>>(this, nameof(IGXHubEvents.AgentStatusChange), (agents) =>
            {
                StateChanged(agents);
            });
            Notifier.Clear();
            Notifier.UpdateButtons();
            if (table != null && Id != null)
            {
                //Get last selected item.
                table.Active = new GXAgent() { Id = Id.Value };
            }
        }
        catch (AccessTokenNotAvailableException exception)
        {
            exception.Redirect();
        }
        catch (Exception ex)
        {
            Notifier?.ProcessError(ex);
        }
    }

    private async ValueTask<ItemsProviderResult<GXAgent>> GetItems(GXItemsProviderRequest request)
    {
        try
        {
            //Don't clear status or error is lost.
            Notifier?.ProgressStart();
            if (request.Removed)
            {
                filter.Removed = DateTimeOffset.MaxValue;
            }
            else
            {
                filter.Removed = null;
            }

            ListAgents req = new ListAgents()
                {
                    Index = request.StartIndex,
                    Count = request.Count,
                    Filter = filter,
                    OrderBy = request.OrderBy,
                    Descending = request.Descending,
                    AllUsers = request.ShowAllUserData
                };
            if (Count != 0)
            {
                req.Count = Count;
            }
            var ret = await Http.PostAsJson<ListAgentsResponse>("api/Agent/List", req, request.CancellationToken);
            ItemsProviderResult<GXAgent> value = new ItemsProviderResult<GXAgent>(ret.Agents, ret.Count);
            return value;
        }
        catch (TaskCanceledException)
        {
            //Let the table component handle this.
            throw;
        }
        catch (Exception ex)
        {
            Notifier?.ProcessError(ex);
        }
        finally
        {
            Notifier?.ProgressEnd();
        }
        return default;
    }

    /// <summary>
    /// Update the selected agents to the new version.
    /// </summary>
    public void OnUpdateVersion()
    {
        try
        {
            if (table == null || !table.SingleOrDefault().Any())
            {
                throw new Exception(Gurux.DLMS.AMI.Client.Properties.Resources.NoItemIsSelected);
            }
            UpdateConfirmation?.Show(table?.SingleOrDefault()?.Select(s => s.Name).ToArray(), Properties.Resources.AgentUpdates);
        }
        catch (Exception ex)
        {
            Notifier?.ProcessError(ex);
        }
    }

    /// <summary>
    /// Update the agent.
    /// </summary>
    public async Task OnUpdateConfirmation(ConfirmArgs args)
    {
        if (table != null && args.Confirm)
        {
            Notifier?.ProgressStart();
            Notifier?.ClearStatus();
            try
            {
                //Version to update.
                InstallAgent req = new InstallAgent();
                foreach (var it in table.SingleOrDefault())
                {
                    req.Agents.Add(new GXAgent()
                        {
                            Id = it.Id,
                            UpdateVersion = it.AvailableVersion,
                            ConcurrencyStamp = it.ConcurrencyStamp
                        });
                }
                await Http.PostAsJson<UpdateAgentResponse>("api/agent/Install", req);
            }
            catch (AccessTokenNotAvailableException exception)
            {
                exception.Redirect();
            }
            catch (Exception ex)
            {
                Notifier?.ProcessError(ex);
            }
            finally
            {
                Notifier?.ProgressEnd();
            }
        }
    }

    /// <summary>
    /// Add new agent.
    /// </summary>
    public void OnAdd()
    {
        try
        {
            ClientHelpers.NavigateTo(NavigationManager, Notifier, "/AgentAdd");
        }
        catch (Exception ex)
        {
            Notifier?.ProcessError(ex);
        }
    }

    /// <summary>
    /// Edit agent.
    /// </summary>
    public void OnEdit()
    {
        try
        {
            Notifier.ClearStatus();
            if (table?.Active == null)
            {
                throw new Exception(Gurux.DLMS.AMI.Client.Properties.Resources.NoItemIsSelected);
            }
            ClientHelpers.NavigateTo(NavigationManager, Notifier, "/Agent/Edit/" + table?.Active.Id);
        }
        catch (AccessTokenNotAvailableException exception)
        {
            exception.Redirect();
        }
        catch (Exception ex)
        {
            Notifier?.ProcessError(ex);
        }
    }

    /// <summary>
    /// Delete selected agents.
    /// </summary>
    public void OnRemove()
    {
        try
        {
            if (table == null && !table.SingleOrDefault().Any())
            {
                throw new Exception(Gurux.DLMS.AMI.Client.Properties.Resources.NoItemIsSelected);
            }
            DeleteConfirmation?.Show(table.SingleOrDefault().Select(s => s.Name).ToArray());
        }
        catch (AccessTokenNotAvailableException exception)
        {
            exception.Redirect();
        }
        catch (Exception ex)
        {
            Notifier?.ProcessError(ex);
        }
    }

    /// <summary>
    /// Remove the agent(s).
    /// </summary>
    /// <param name="args">Confirm arguments.</param>
    public async Task OnDeleteConfirmation(ConfirmArgs args)
    {
        try
        {
            if (table != null && args.Confirm)
            {
                await Http.PostAsJson<RemoveAgentResponse>("api/Agent/Delete",
                    new RemoveAgent()
                        {
                            Delete = args.Delete,
                            Ids = table.SingleOrDefault().Select(w => w.Id).ToArray()
                        });
            }
        }
        catch (AccessTokenNotAvailableException exception)
        {
            exception.Redirect();
        }
        catch (Exception ex)
        {
            Notifier?.ProcessError(ex);
        }
    }

    public void Dispose()
    {
        Notifier.RemoveListener(this);
    }
}
